package commons.base.db;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

/**
 * @author yuan<cihang.yuan@happyelements.com>
 *
 * @param <T>
 */
public abstract class BaseTableDao<T> {

	protected String dbInstance;
	protected Class<T> modelClass;
	protected Map<String, Method> writeMethodMap = new HashMap<String, Method>();
	protected boolean supportCustomFill;
	
	@SuppressWarnings("unchecked")
	public BaseTableDao(String dbInstance, boolean supportCustomFill){
		this.dbInstance = dbInstance;
		this.supportCustomFill = supportCustomFill;
		
		Type type = this.getClass().getGenericSuperclass();
		if(type instanceof ParameterizedType){
			modelClass = (Class<T>)((ParameterizedType)type).getActualTypeArguments()[0];
			
			try {
				BeanInfo beanInfo = Introspector.getBeanInfo(modelClass);
				for(PropertyDescriptor pd  : beanInfo.getPropertyDescriptors()){
					writeMethodMap.put(pd.getName(), pd.getWriteMethod());
				}
			} catch (IntrospectionException e) {
				e.printStackTrace();
			}
		}
	}
	
	protected void fillModel(ResultSet rs, T model){
		
	}
	
	public <E> E queryField(String sql, Object...params)throws SQLException{
    	return Databases.getDatabase(dbInstance).queryField(sql, params);
	}
	
	public <E> List<E> queryColumn(String sql, Object...params)throws SQLException{
		return Databases.getDatabase(dbInstance).queryColumn(sql, params);
	}
	
	public Map<String, Object> queryMultiField(String sql, Object...params)throws SQLException{
		return Databases.getDatabase(dbInstance).queryMultiField(sql, params);
	}
	
	public List<Map<String, Object>> queryMultiColumn(String sql, Object...params)throws SQLException{
		return Databases.getDatabase(dbInstance).queryMultiColumn(sql, params);
	}
	
	public T queryModel(String sql, Object...params)throws SQLException{
		return Databases.getDatabase(dbInstance).getQr().query(sql, new BeanHandler<T>(modelClass){
		    @Override
		    public T handle(ResultSet rs) throws SQLException {
		    	if(supportCustomFill){
		    		try {
		    			if(rs.next()){
		    				T model = modelClass.newInstance();
							fillModel(rs, model);
							return model;
		    			}
						return null;
					} catch (InstantiationException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
		    	}
		    	
		        return super.handle(rs);
		    }
		}, params);
	}
	
	public List<T> queryModelList(String sql, Object...params)throws SQLException{
		return Databases.getDatabase(dbInstance).getQr().query(sql, new BeanListHandler<T>(modelClass){
			@Override
		    public List<T> handle(ResultSet rs) throws SQLException {
				if(supportCustomFill){
					List<T> list = new ArrayList<T>();
					
					while(rs.next()){
						try {
							T model = modelClass.newInstance();
							fillModel(rs, model);
							list.add(model);
						} catch (InstantiationException e) {
							e.printStackTrace();
						} catch (IllegalAccessException e) {
							e.printStackTrace();
						}
					}
					
					return list;
				}
				
		        return super.handle(rs);
		    }
		}, params);
	}
	
	public int update(String sql, Object...params)throws SQLException{
		return Databases.getDatabase(dbInstance).update(sql, params);
	}
	
	public long insertReturnId(String sql, Object...params)throws SQLException{
		return Databases.getDatabase(dbInstance).insertReturnId(sql, params);
	}
	
}
